/******************************************************************************/
/*  Class PDU                                                                 */
/*  PO: S. Maslyakov, rusoil.9@gmail.com                                      */
/*                                                                            */
/*  Revision:     1.0                                                         */
/*  Date:         2011/01/06 16:08:33                                         */
/******************************************************************************/


#include "data_types.h"
#include "Core\core.h"
#include "Device\device.h"
#include "Auto\automem.h"
#include "Lib\bin_to_hex.h"
#include "Pdu\pdu.h"


// Gsm default alphabet
static const uint8_t charset[] =
{
    '@' , 0x00, // COMMERCIAL AT
    0xA3, 0x01, // POUND SIGN
    '$' , 0x02, // DOLLAR SIGN
    0xA5, 0x03, // YEN SIGN
    0xE8, 0x04, // LATIN SMALL LETTER E WITH GRAVE
    0xE9, 0x05, // LATIN SMALL LETTER E WITH ACUTE
    0xF9, 0x06, // LATIN SMALL LETTER U WITH GRAVE
    0xEC, 0x07, // LATIN SMALL LETTER I WITH GRAVE
    0xF2, 0x08, // LATIN SMALL LETTER O WITH GRAVE
    0xE7, 0x09, // LATIN SMALL LETTER C WITH CEDILLA
    0xC7, 0x09, // LATIN CAPITAL LETTER C WITH CEDILLA
    0x0A, 0x0A, // LF
    0xD8, 0x0B, // LATIN CAPITAL LETTER O WITH STROKE
    0xF8, 0x0C, // LATIN SMALL LETTER O WITH STROKE
    0x0D, 0x0D, // CR
    0xC5, 0x0E, // LATIN CAPITAL LETTER A WITH RING ABOVE
    0xE5, 0x0F, // LATIN SMALL LETTER A WITH RING ABOVE
    0x81, 0x10, // GREEK CAPITAL LETTER DELTA
    0x5F, 0x11, // LOW LINE
    0x82, 0x12, // GREEK CAPITAL LETTER PHI
    0x83, 0x13, // GREEK CAPITAL LETTER GAMMA
    0x84, 0x14, // GREEK CAPITAL LETTER LAMDA
    0x85, 0x15, // GREEK CAPITAL LETTER OMEGA
    0x86, 0x16, // GREEK CAPITAL LETTER PI
    0x87, 0x17, // GREEK CAPITAL LETTER PSI
    0x88, 0x18, // GREEK CAPITAL LETTER SIGMA
    0x89, 0x19, // GREEK CAPITAL LETTER THETA
    0x8A, 0x1A, // GREEK CAPITAL LETTER XI
    0x1B, 0x1B, // ESC
    0xC6, 0x1C, // LATIN CAPITAL LETTER AE
    0xE6, 0x1D, // LATIN SMALL LETTER AE
    0xDF, 0x1E, // LATIN SMALL LETTER SHARP S
    0xC9, 0x1F, // LATIN CAPITAL LETTER E WITH ACUTE
    ' ' , 0x20, // SPACE
    '!' , 0x21, // EXCLAMATION MARK
    0x22, 0x22, // QUOTATION MARK
    '#' , 0x23, // NUMBER SIGN
    '!' , 0x24, // This character is never sent.
    '%' , 0x25, // PERSENT SIGN
    '&' , 0x26, // AMPERSAND
    0x27, 0x27, // APOSTROPHE
    '(' , 0x28, // LEFT PARENTHESIS
    ')' , 0x29, // RIGHT PARENTHESIS
    '*' , 0x2A, // ASTERISK
    '+' , 0x2B, // PLUS SIGN
    ',' , 0x2C, // COMMA
    '-' , 0x2D, // HYPHEN-MINUS
    '.' , 0x2E, // FULL STOP
    '/' , 0x2F, // SOLIDUS
    '0' , 0x30, // DIGIT 0...9
    '1' , 0x31,
    '2' , 0x32,
    '3' , 0x33,
    '4' , 0x34,
    '5' , 0x35,
    '6' , 0x36,
    '7' , 0x37,
    '8' , 0x38,
    '9' , 0x39,
    ':' , 0x3A, // COLON
    ';' , 0x3B, // SEMICOLON
    '<' , 0x3C, // LESS-THAN SIGN
    '=' , 0x3D, // EQUALS SIGN
    '>' , 0x3E, // GREATER-THAN SIGN
    '?' , 0x3F, // QUESTION MARK
    0xA1, 0x40, // INVERTED EXCLAMATION MARK
    'A' , 0x41, // LATIN CAPITAL LETTER A...Z
    'B' , 0x42,
    'C' , 0x43,
    'D' , 0x44,
    'E' , 0x45,
    'F' , 0x46,
    'G' , 0x47,
    'H' , 0x48,
    'I' , 0x49,
    'J' , 0x4A,
    'K' , 0x4B,
    'L' , 0x4C,
    'M' , 0x4D,
    'N' , 0x4E,
    'O' , 0x4F,
    'P' , 0x50,
    'Q' , 0x51,
    'R' , 0x52,
    'S' , 0x53,
    'T' , 0x54,
    'U' , 0x55,
    'V' , 0x56,
    'W' , 0x57,
    'X' , 0x58,
    'Y' , 0x59,
    'Z' , 0x5A,
    0xC4, 0x5B, // LATIN CAPITAL LETTER A WITH DIAERESIS
    0xD6, 0x5C, // LATIN CAPITAL LETTER O WITH DIAERESIS
    0xD1, 0x5D, // LATIN CAPITAL LETTER N WITH TILDE
    0xDC, 0x5E, // LATIN CAPITAL LETTER U WITH DIAERESIS
    0xA7, 0x5F, // SECTION SIGN
    0xBF, 0x60, // INVERTED QUESTION MARK
    'a' , 0x61, // LATIN SMALL LETTER A...Z
    'b' , 0x62,
    'c' , 0x63,
    'd' , 0x64,
    'e' , 0x65,
    'f' , 0x66,
    'g' , 0x67,
    'h' , 0x68,
    'i' , 0x69,
    'j' , 0x6A,
    'k' , 0x6B,
    'l' , 0x6C,
    'm' , 0x6D,
    'n' , 0x6E,
    'o' , 0x6F,
    'p' , 0x70,
    'q' , 0x71,
    'r' , 0x72,
    's' , 0x73,
    't' , 0x74,
    'u' , 0x75,
    'v' , 0x76,
    'w' , 0x77,
    'x' , 0x78,
    'y' , 0x79,
    'z' , 0x7A,
    0xE4, 0x7B, // LATIN SMALL LETTER A WITH DIAERESIS
    0xF6, 0x7C, // LATIN SMALL LETTER O WITH DIAERESIS
    0xF1, 0x7D, // LATIN SMALL LETTER N WITH TILDE
    0xFC, 0x7E, // LATIN SMALL LETTER U WITH DIAERESIS
    0xE0, 0x7F, // LATIN SMALL LETTER A WITH GRAVE
    0   , 0     // End marker
};



//==============================================================================
// App: Convert ascii char to gsm
//==============================================================================
static uint8_t char2gsm(const uint8_t ch)
{
    uint32_t table_row = 0;

    // search in translation table
    while (charset[table_row * 2]) {
        if (charset[table_row * 2] == ch) {
            return charset[table_row * 2 + 1];
        };
        table_row++;
    };

    return '!';
}
//==============================================================================
//==============================================================================


//==============================================================================
// App: Convert gsm in ascii char
//==============================================================================
static uint8_t gsm2char(const uint8_t ch)
{
    uint32_t table_row = 0;

    while (charset[table_row * 2]) {
        if (charset[table_row * 2 + 1] == ch) {
            return charset[table_row * 2];
        };
        table_row++;
    };

    return '!';
}
//==============================================================================
//==============================================================================


//==============================================================================
// App: Convert septets(7-bit data) into octets
//==============================================================================
void PDU::SeptetsToOctets(__DATA_PDU * const _pSrc, __DATA_PDU * const _pDst) {

    if ((_pSrc->dataLen == 0) || (_pSrc->pData == 0)) {
        return;
    };

    // Convert ascii to gsm
    for (uint32_t i = 0; i < _pSrc->dataLen; i++) {
        _pSrc->pData[i] = char2gsm(_pSrc->pData[i]);
    };

    uint8_t c, w;
    uint32_t shift;

    _pDst->dataLen = 0;
    shift = 0;

    if (_pSrc->dataLen == 1) {
        _pDst->pData[_pDst->dataLen++] = _pSrc->pData[0];
        return;
    };

    for (uint32_t i = 0; i < _pSrc->dataLen; i++) {

        c = _pSrc->pData[i] & 0x7f;
        c >>= shift;

        w = _pSrc->pData[i + 1] & 0x7f;
        w <<= (7 - shift++);
        c |= w;

        if (shift == 7) {
            shift = 0;
            i++;
        };

        _pDst->pData[_pDst->dataLen++] = c;
        _pDst->pData[_pDst->dataLen + 1] = 0;
    };
}
//==============================================================================
//==============================================================================


//==============================================================================
// App: Convert octets into septets
//==============================================================================
void PDU::OctetsToSeptets(const __DATA_PDU * const _pSrc, __DATA_PDU * const _pDst) {
    uint32_t bitCount;
    uint32_t i;
    uint8_t byte;

    if (_pSrc->dataLen == 0) {
        return;
    };

    _pDst->dataLen = 0;
    byte = _pSrc->pData[0];
    byte >>= 7;
    _pDst->pData[_pDst->dataLen++] = _pSrc->pData[0] & 0x7F;

    for (i = 1, bitCount = 1; i < _pSrc->dataLen; i++) {

        _pDst->pData[_pDst->dataLen] = (_pSrc->pData[i] << bitCount) | byte;
        _pDst->pData[_pDst->dataLen] &= 0x7F;

        // Convert gsm to ascii
        _pDst->pData[_pDst->dataLen] = gsm2char(_pDst->pData[_pDst->dataLen]);

        byte = _pSrc->pData[i];
        byte >>= (7 - bitCount);

        // Inc bitCount and k
        if ((++bitCount == 7) && ((i + 1) < _pSrc->dataLen)) {
            _pDst->pData[++_pDst->dataLen] = byte;
            bitCount = 1;

            // Convert gsm to ascii
            _pDst->pData[_pDst->dataLen] = gsm2char(_pDst->pData[_pDst->dataLen]);

            byte = _pSrc->pData[++i];
            byte >>= 7;
            _pDst->pData[++_pDst->dataLen] = _pSrc->pData[i] & 0x7F;

            // Convert gsm to ascii
            _pDst->pData[_pDst->dataLen] = gsm2char(_pDst->pData[_pDst->dataLen]);
        };
        _pDst->dataLen++;
    };
    _pDst->pData[_pDst->dataLen] = 0;
}
//==============================================================================
//==============================================================================


//==============================================================================
// App: Convert txt num to bin-dec num
//==============================================================================
uint32_t PDU::ConvertTxtNumToPduNum(const int8_t * const _pNum, uint8_t * const _pDst) {
    uint32_t dataLen = 0;

    if (_pNum == 0) {
        return 0;
    };

    const uint16_t len = strlen(_pNum);

    if ((len > 11) || (len == 0)) {
        return 0;
    };

    // Convert num (79277038969 -> 9772078369F9)
    for (uint16_t i = 0; i < (len - 1); i++) {
        if ((_pNum[i] > '9') || (_pNum[i] < '0')
            || (_pNum[i + 1] > '9') || (_pNum[i + 1] < '0')) {
            return 0;
        };

        _pDst[dataLen] = _pNum[i] - '0';
        _pDst[dataLen] |= ((_pNum[i + 1] - '0') << 4);

        dataLen++;
        i++;
    };

    if (len % 2) {
        _pDst[dataLen] = 0xF0;
        _pDst[dataLen] |= _pNum[len - 1] - '0';
        dataLen++;
    };

    return dataLen;
}
//==============================================================================
//==============================================================================


//==============================================================================
// App: Convert bin-dec to txt
//==============================================================================
uint32_t PDU::ConvertBinDecToTxt(const __DATA_PDU * const _pSrc, uint8_t * const _pDst) {

    if ((_pSrc->pData == 0) || (_pSrc->dataLen == 0)) {
        return 0;
    };

    uint32_t dataLen = 0;

    // Convert (9772078369F9 -> 79277038969)
    for (uint32_t i = 0; i < _pSrc->dataLen; i++) {
        if (((_pSrc->pData[i] & 0x0F) > 9) || ((_pSrc->pData[i] & 0x0F) < 0)
            || ((_pSrc->pData[i] >> 4) > 9) || ((_pSrc->pData[i] >> 4) < 0)) {
            if (i != (_pSrc->dataLen - 1)) {
                return 0;
            };
        };

        _pDst[dataLen++] = (_pSrc->pData[i] & 0x0F) + '0';
        _pDst[dataLen++] = (_pSrc->pData[i] >> 4) + '0';
    };

    if (_pDst[dataLen - 1] == (0xF + '0')) {
        dataLen--;
    };
    _pDst[dataLen] = 0;

    return dataLen;
}
//==============================================================================
//==============================================================================


//==============================================================================
// App: Create PDU sms
//==============================================================================
bool_t PDU::CreateSms(const __SMS_CODE_PDU * const _pSmsData, __DATA_PDU * const _pDst) {

    AUTOMEM mem(DEVICE::Get_mem_pool_128());
    uint8_t * pBuff;

    if ((_pSmsData->txtData.dataLen > 70) || (_pSmsData->txtData.dataLen == 0)) {
        return FALSE_T;
    };

    // Allocate free block
    if (mem.AllocMemBlock(TN_MSEC_TO_TICK(500)) == 0) {
        return FALSE_T;
    };

    pBuff = (uint8_t *)mem.GetMemBlock();
    _pDst->dataLen = 0;

    // Service center num
    if ((_pSmsData->pScNum == 0) || (_pSmsData->pScNum[0] == 0)) {
        _pDst->pData[_pDst->dataLen++] = '0';
        _pDst->pData[_pDst->dataLen++] = '0';
    }
    else {
        const uint32_t len = ConvertTxtNumToPduNum(_pSmsData->pScNum, pBuff);

        if (len == 0) {
            return FALSE_T;
        };

        // len sc num - octets number
        sprintf((int8_t *)_pDst->pData + _pDst->dataLen, "%.02X", len + 1);
        _pDst->dataLen += 2;

        // Type of address
        _pDst->pData[_pDst->dataLen++] = '9';
        _pDst->pData[_pDst->dataLen++] = '1';

        // Convert num to hex
        for (uint32_t i = 0; i < len; i++) {
            sprintf((int8_t *)_pDst->pData + _pDst->dataLen, "%.02X", pBuff[i]);
            _pDst->dataLen += 2;
        };
    };

    // TP
    _pDst->pData[_pDst->dataLen++] = '0';
    _pDst->pData[_pDst->dataLen++] = '1';

    // TP msg reference
    _pDst->pData[_pDst->dataLen++] = '0';
    _pDst->pData[_pDst->dataLen++] = '0';

    // Dst num
    if ((_pSmsData->pDstNum == 0) || (_pSmsData->pDstNum[0] == 0)) {
        return FALSE_T;
    }
    else {
        const uint32_t len = ConvertTxtNumToPduNum((_pSmsData->pDstNum[0] == '+')
                                   ? (_pSmsData->pDstNum + 1) : _pSmsData->pDstNum
                                   , pBuff);

        if (len == 0) {
            return FALSE_T;
        };

        // len dst num - dig number
        sprintf((int8_t *)_pDst->pData + _pDst->dataLen, "%.02X", strlen(_pSmsData->pDstNum));
        _pDst->dataLen += 2;

        // Type of address
        _pDst->pData[_pDst->dataLen++] = '9';
        _pDst->pData[_pDst->dataLen++] = '1';

        // Convert num to hex
        for (uint32_t i = 0; i < len; i++) {
            sprintf((int8_t *)_pDst->pData + _pDst->dataLen, "%.02X", pBuff[i]);
            _pDst->dataLen += 2;
        };
    };

    // Protocol pid
    _pDst->pData[_pDst->dataLen++] = '0';
    _pDst->pData[_pDst->dataLen++] = '0';

    // Data coding scheme - GSM 7-bit
    _pDst->pData[_pDst->dataLen++] = '0';
    _pDst->pData[_pDst->dataLen++] = '0';

    // TP validaty period - optional
    //_pDst->pData[_pDst->dataLen++] = 'A';
    //_pDst->pData[_pDst->dataLen++] = 'A';

    // Msg len - only for septets (7-bit data)
    sprintf((int8_t *)_pDst->pData + _pDst->dataLen, "%.02X", _pSmsData->txtData.dataLen);
    _pDst->dataLen += 2;

    // Convert msg
    __DATA_PDU dstMsg;
    dstMsg.pData = pBuff;

    SeptetsToOctets((__DATA_PDU *)&(_pSmsData->txtData), &dstMsg);

    if (dstMsg.dataLen == 0) {
        return FALSE_T;
    };

    // Convert data to hex
    for (uint32_t i = 0; i < dstMsg.dataLen; i++) {
        sprintf((int8_t *)_pDst->pData + _pDst->dataLen, "%.02X", dstMsg.pData[i]);
        _pDst->dataLen += 2;
    };

    return TRUE_T;
}
//==============================================================================
//==============================================================================


//==============================================================================
// App: Decode PDU sms
//==============================================================================
bool_t PDU::DecodeSms(const __DATA_PDU * const _pPduSms, __SMS_DECODE_PDU * const _pDst) {

    if (_pPduSms->dataLen < 27) {
        return FALSE_T;
    };

    __DATA_PDU data;
    uint32_t len;
    uint32_t count = 0;

    // Service center num
    len = _pPduSms->pData[count];
    count += 1;

    if (len == 0) {
        return FALSE_T;
    };

    // type sc num
    count += 1;

    data.pData = _pPduSms->pData + count;
    data.dataLen = len - 1;

    if (ConvertBinDecToTxt(&data, (uint8_t *)_pDst->pScNum) == 0) {
        return FALSE_T;
    };
    count += len - 1;

    // SMS-DELIVER
    count += 1;

    // Sender num
    len = _pPduSms->pData[count];
    count += 1;

    if (len == 0) {
        return FALSE_T;
    };

    // type sender num
    count += 1;

    data.pData = _pPduSms->pData + count;
    data.dataLen = (len % 2) ? ((len + 1)/2) : (len/2);

    if (ConvertBinDecToTxt(&data, (uint8_t *)_pDst->pSenderNum) == 0) {
        return FALSE_T;
    };
    count += (len % 2) ? ((len + 1)/2) : (len/2);

    // PID protocol identifer
    count += 1;

    // Data coding sheme
    len = _pPduSms->pData[count];

    if (len != 0) {
        return FALSE_T;
    };

    count += 1;

    // Timestamp
    data.pData = _pPduSms->pData + count;
    data.dataLen = 6;

    if (ConvertBinDecToTxt(&data, (uint8_t *)_pDst->pTimeStamp) == 0) {
        return FALSE_T;
    };
    count += 7;

    // Msg
    len = _pPduSms->pData[count];
    count += 1;

    data.pData = _pPduSms->pData + count;
    data.dataLen = _pPduSms->dataLen - count;

    OctetsToSeptets(&data, &(_pDst->data));

    if (_pDst->data.dataLen != len) {
        return FALSE_T;
    };
    _pDst->data.pData[len] = 0;

    return TRUE_T;
}
//==============================================================================
//==============================================================================